Flutter

您所在的位置:网站首页 flutter tabview新闻列表 Flutter

Flutter

2023-08-16 22:54| 来源: 网络整理| 查看: 265

这是我参与11月更文挑战的第24天,活动详情查看:2021最后一次更文挑战

在聊天界面添加一个搜索框。那么就在ListView里面添加一个cell,那么就需要itemCount里面加1。

itemCount: _datas.length + 1,

创建一个chat package,然后将chat_page拖进来并且重新创建一个search_cell文件。

在这里插入图片描述

将ListView里面的itemBuilder的代码抽取成一个方法,然后在里面判断如果index == 0 则返回 SearchCell。这里后面需要index--,否则index就会从1开始。

在这里插入图片描述

接下来写search cell 的界面,先赋予一个高度和颜色,来看是否能显示。

return Container( height: 45, width: 200, color: Colors.red, );

运行后看到显示了出来。

在这里插入图片描述

搜索框需要响应点击时间,所以这里需要用GestureDetector包一下,然后颜色改为weChatThemColor,里面用Stack来写。

return GestureDetector( child: Container( height: 45, width: 200, color: weChatThemColor, padding: EdgeInsets.all(5), child: Stack( children: [ ], ), ), );

在stack里面先添加一个白底

Container( decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(6.0), ), ),//白底

然后添加Row里面放图片和文字,为了让其居中设置row的mainAxisAlignment为MainAxisAlignment.center,而让row居中则将stack的alignment改为Alignment.center。

child: Stack( alignment: Alignment.center, children: [ Container( decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(6.0), ), ),//白底 Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Image(image: AssetImage('images/放大镜b.png'),width: 15,color: Colors.grey,), Text(' 搜索',style: TextStyle(fontSize: 15,color: Colors.grey),), ],), ], ),

这样搜索框页面就完成了

在这里插入图片描述

接下来点进去需要到一个新的界面,那么就添加onTap,然后创建一个文件来写新页面SearchPage。

import 'package:flutter/material.dart'; class SearchPage extends StatefulWidget { const SearchPage({Key? key}) : super(key: key); @override _SearchPageState createState() => _SearchPageState(); } class _SearchPageState extends State { @override Widget build(BuildContext context) { return Scaffold( body: Column( children: [ SearchBar(), Expanded( flex: 1, child: ListView.builder( itemBuilder: itemBuilder, itemCount: 3, ), ), ], ), ); } Widget itemBuilder(BuildContext context, int index) { return Container( child:Text("Column$index"), ); } } class SearchBar extends StatefulWidget { const SearchBar({Key? key}) : super(key: key); @override _SearchBarState createState() => _SearchBarState(); } class _SearchBarState extends State { @override Widget build(BuildContext context) { return Container( height: 84, color: weChatThemColor, ); } }

然后在SearchCell的GestureDetector里面添加页面push。

onTap: (){ Navigator.of(context).push(MaterialPageRoute( builder: (BuildContext context) => SearchPage())); },

运行后得到下面的界面:

在这里插入图片描述

发现这里有间距,那么就使用removePadding将ListView头部间距去掉。

child: MediaQuery.removePadding( removeTop: true, context: context, child: ListView.builder( itemBuilder: itemBuilder, itemCount: 3, ), ),

然后开始编写SearchBar。

return Container( height: 84, color: weChatThemColor, child: Column( children: [ SizedBox(height: 40,), Container( height: 44, color:Colors.red, child: Row( children: [ Container( width: screenWidth(context) - 40, height: 34, color: Colors.yellow, ),//圆角背景 Text('取消'),//取消按钮 ], ), ), ], ), );

运行后看到:

在这里插入图片描述

然后在添加圆角,间距以及按钮等。

return Container( height: 84, color: weChatThemColor, child: Column( children: [ SizedBox( height: 40, ), Container( height: 44, color: Colors.red, child: Row( children: [ Container( width: screenWidth(context) - 50, height: 34, margin: EdgeInsets.only(left: 5, right: 5), padding: EdgeInsets.only(left: 5, right: 5), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(6.0), ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Image( image: AssetImage('images/放大镜b.png'), width: 20, color: Colors.grey, ), //放大镜 Icon(Icons.cancel), ], ), ), //圆角背景 Text('取消'), //取消按钮 ], ), ), ], ), );

然后还需要添加TextField。

return Container( height: 84, color: weChatThemColor, child: Column( children: [ SizedBox( height: 40, ), Container( height: 44, child: Row( children: [ Container( width: screenWidth(context) - 50, height: 34, margin: EdgeInsets.only(left: 5, right: 5), padding: EdgeInsets.only(left: 5, right: 5), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(6.0), ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: const [ Image( image: AssetImage('images/放大镜b.png'), width: 20, color: Colors.grey, ), //放大镜 Expanded( child: TextField( cursorColor: Colors.green, autofocus: true, style: TextStyle( fontSize: 18.0, color: Colors.black, fontWeight: FontWeight.w300, ), decoration: InputDecoration( contentPadding: EdgeInsets.only(left: 5,bottom: 10), border:InputBorder.none, hintText:'搜索', ), ), ), Icon(Icons.cancel,size: 20,color: Colors.grey,), ], ), ), //圆角背景 Text('取消'), //取消按钮 ], ), ), ], ), );

之前用Expanded包着ListView是因为ListView没有大小,当我们设置了 shrinkWrap: true之后,那么就会根据ListView的内容大小来展示,就不需要Expanded了。我们这里还是用之前的写法,让ListView占满屏幕下面部分。

return Scaffold( body: Column( children: [ SearchBar(), ListView.builder( shrinkWrap: true, itemBuilder: itemBuilder, itemCount: 3, ), ], ), );

接下来处理点击和逻辑事件

这里先为取消添加一个pop的点击事件。

GestureDetector( child: Text('取消'), onTap: () { Navigator.pop(context); }, ),

接下来需要监听搜索框,当没有任何东西的时候,那么就不显示cancel icon。创建一个TextEditingController变量。

final TextEditingController _textEditingController = TextEditingController();

为TextField添加Controller

controller: _textEditingController,

然后监听TextField的onChanged

onChanged: _onChanged, void _onChanged(value) {}

然后创建_showClear来判断是否显示TextField后面的取消按钮,默认为false。

bool _showClear = false;

在_onChanged方法里面进行字符串长度的判断以及_showClear的赋值。

void _onChanged(String text) { if (text.length > 0) { setState(() { _showClear = true; }); } else { setState(() { _showClear = false; }); } }

根据_showClear的值觉得是否显示Icon。

if (_showClear) Icon( Icons.cancel, size: 20, color: Colors.grey, )

为 cancel Icon添加点击手势清空输入内容。

if (_showClear) GestureDetector( child: Icon( Icons.cancel, size: 20, color: Colors.grey, ), onTap: () { _textEditingController.clear(); setState(() { _onChanged(""); }); }, )

这样页面部分就完成了。这里有个问题,ChatPage点击进去SearchPage的时候那么SearchPage一定要有数据,那么SearchBar 是否有数据呢?这里有两种情况,一种是有,将数据传给SearchBar,搜索完之后将结果返回给SearchPage,第二种是没有,SearchBar直接通过回调将输入内容给SearchPage。 先在SearchCell添加

final List? datas; const SearchCell({ this.datas});

然后在ChatPage为其赋值,这样数据就给了SearchCell。

if (index == 0) { return SearchCell(datas: _datas,); }

然后再传给SearchPage。在SearchPage添加:

final List? datas; const SearchPage({ this.datas});

然后在SearchCell进来的地方将data传进来

SearchPage(datas: datas,)

在SearchBar里面添加一个回调:

const SearchBar({Key? key, this.onChanged}) : super(key: key); final ValueChanged? onChanged;

然后在_onChanged里面调用这个回调。这里if else 判断太长了,可以直接使用 _showClear = text.length > 0进行设值。

void _onChanged(String text) { if (widget.onChanged != null) { widget.onChanged!(text); } setState(() { _showClear = text.length > 0; }); }

这个时候SearchPage就可以传回调了

SearchBar(onChanged: ( String text){},),

创建一个 _searchData方法来处理回调传过来的文字。

SearchBar(onChanged: (String text){ _searchData(text); },), void _searchData(String text) { }

接下来创建一个数组来装符合条件的数据

List _models = [];

_searchData里面循环检索然后添加符合条件的数据到_models。

void _searchData(String text) { _models.clear(); // 每次搜索先情况 if (text.length > 0 ) { if (widget.datas != null){ // 循环检索 for (int i = 0; i < widget.datas!.length;i++ ) { String? name = widget.datas![i].name; if ((name ?? "").contains(text)) { _models.add(widget.datas![i]); } } } } setState(() { }); }

接下来根据_models显示ListView的内容,将itemCount改为_models.length。

itemCount: _models.length,

在itemBuilder里面返回

Widget itemBuilder(BuildContext context, int index) { return Container( color: Colors.red, child: Text("${_models[index].name}"), ); }

这样搜索就可以显示符合条件的Model了。

在这里插入图片描述

接下来需要来编写Cell的界面,这里可以直接把之前聊天界面的样式复制过来。

ListTile( title: Text(_models[index].name ?? ""), subtitle: Container( alignment: Alignment.bottomCenter, padding: EdgeInsets.only(right: 10), height: 25, child: Text( _models[index].message ?? "", overflow: TextOverflow.ellipsis, ), ), leading: ClipRRect( //剪裁为圆角矩形 borderRadius: BorderRadius.circular(5.0), child: Image(image: NetworkImage(_models[index].imageUrl ?? "")), ), );

运行后:

在这里插入图片描述

这里需要对名字做高亮处理,那么就把title部分提取出来做处理。

title: _title(_models[index].name ?? ""), Text _title(String name) { return Text(name); }

我们要知道搜索的名字是什么,所以这里声明一个变量

String _searchStr = "";

然后在_searchData里面赋值。

_searchStr = text

然后在声明2个TextStyle

TextStyle _normalStyle = TextStyle(fontSize: 16,color: Colors.black); TextStyle _highLightedStyle = TextStyle(fontSize: 16,color: Colors.green);

然后再_title方法里面处理。

idget _title(String name) { List spans = []; List strs = name.split(_searchStr); for (int i = 0; i < strs.length; i ++) { String str = strs[i]; print(strs); if (str == "" && i < strs.length - 1) { print('here1'); spans.add(TextSpan(text: _searchStr,style:_highLightedStyle)); } else { print('here2'); spans.add(TextSpan(text: str,style:_normalStyle)); if (i < strs.length - 1) { print('here3'); spans.add(TextSpan(text: _searchStr,style:_highLightedStyle)); } } }

这样搜索高亮就完成了。

在这里插入图片描述



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3